package cn.hy.shortUrl.util;


import org.apache.commons.lang3.RandomUtils;
/**
 * 如何保证key 的唯一性
 * 1. 方案1: 直接将长链 通过 MurmurHash 计算,得到一个32位 int 类型
 * 2. 方案2： key 自增: 单表场景，直接使用数据库自增 id 就能实现 id 唯一。多库多表，大家肯定都有一个全局发号器来生成唯一 id。
 *
 */
public class KeyUtils {

    private static final String BASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    /**
     *  另外，建议 id 从一个中等模式的大小开始，如 100w，而不是从 1 开始，这个应该很好理解。
     *
     * 999998 和 999999 转换为 62进制以后，分别为 4C90 和 4C91
     * 我们 10 进制的 999998 和 999999 就可能被转换为 16U06 和 XpJX。因为有随机位的存在，所以会有好几种可能。
     */
    public static String toBase62(long num) {
        // 解决id 顺序的问题
        num = insertRandomBitPer5Bits(num);
        StringBuilder sb = new StringBuilder();
        int targetBase = BASE.length();
        do {
            int i = (int) (num % targetBase);
            sb.append(BASE.charAt(i));
            num /= targetBase;
        } while (num > 0);

        return sb.reverse().toString();
    }


    /**
     * 得到 id 以后，先在其二进制表示的固定位置插入随机位。
     * ，从低位开始，每 5 位后面插入一个随机位，直到高位都是 0 就不再插入
     *
     * 由于我们会浪费掉一些位，所以最大可以表示的数会受影响，不过 64 位的 long 值是一个很大的数，是允许我们奢侈浪费一些的。
     * 还有，前面提到高位为 0 就不再插入，那是为了不至于一开始就往高位插入了 1 导致我们刚开始的值就特别大，转换出来需要更长的字符串。
     */
    private static long insertRandomBitPer5Bits(long val) {
        long result = val;
        long high = val;
        for (int i = 0; i < 10; i++) {
            if (high == 0) {
                break;
            }
            int pos = 5 + 5 * i + i;
            high = result >> pos;
            result = ((high << 1 | RandomUtils.nextInt(0, 2)) << pos)
                    | (result & (-1L >>> (64 - pos)));
        }
        return result;
    }

    // 用不上
    public static long toBase10(String input) {
        int srcBase = BASE.length();
        long id = 0;
        String r = new StringBuilder(input).reverse().toString();

        for (int i = 0; i < r.length(); i++) {
            int charIndex = BASE.indexOf(r.charAt(i));
            id += charIndex * (long) Math.pow(srcBase, i);
        }

        return id;
    }

    public static void main(String[] args) {


        String s = toBase62(999998);
        System.out.println(s);
        String s2 = toBase62(999999);
        System.out.println(s2);



    }
}
